using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Diagnostics;

namespace DiscoveryServer
{
    class Program
    {
        private const int ListenPort = 36669;
        private const string RequestMagic = "IPP_DISCOVERY_REQUEST";

        static void Main(string[] args)
        {
            Console.Title = "DiscoveryServer - UDP 发现服务端 (.NET Framework 4.6)";
            Console.WriteLine("[Server] 启动 UDP 发现服务，端口 {0} ...", ListenPort);

            TryEnsureFirewallRule();

            UdpClient udp = null;
            try
            {
                udp = new UdpClient(ListenPort);
                udp.EnableBroadcast = true;

                while (true)
                {
                    IPEndPoint remoteEp = new IPEndPoint(IPAddress.Any, 0);
                    byte[] data = udp.Receive(ref remoteEp);
                    string text = Encoding.UTF8.GetString(data ?? new byte[0]);

                    Console.WriteLine("[Server] 收到来自 {0} 的数据: {1}", remoteEp, text);

                    if (!string.Equals(text, RequestMagic, StringComparison.Ordinal))
                    {
                        continue;
                    }

                    string localIp = GetLocalIpAddress(remoteEp.Address) ?? "127.0.0.1";

                    string printersPart =
                        "Office-Printer-1,http://" + localIp + ":8631/printers/office-1" +
                        "|" +
                        "Office-Printer-2,http://" + localIp + ":8631/printers/office-2";

                    string response =
                        "IPP_SERVER|" +
                        "Name=" + Environment.MachineName +
                        ";IP=" + localIp +
                        ";Port=8631" +
                        ";Printers=" + printersPart;

                    byte[] respBytes = Encoding.UTF8.GetBytes(response);
                    udp.Send(respBytes, respBytes.Length, remoteEp);

                    Console.WriteLine("[Server] 已回复 {0}: {1}", remoteEp, response);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("[Server] 出错: " + ex);
            }
            finally
            {
                if (udp != null)
                {
                    udp.Close();
                }
            }
        }

        private static string GetLocalIpAddress(IPAddress remoteIp)
        {
            try
            {
                string hostName = Dns.GetHostName();
                IPAddress[] addrs = Dns.GetHostAddresses(hostName);
                IPAddress selected = null;

                // 只考虑 IPv4
                System.Collections.Generic.List<IPAddress> ipv4 = new System.Collections.Generic.List<IPAddress>();
                foreach (var addr in addrs)
                {
                    if (addr.AddressFamily == AddressFamily.InterNetwork)
                    {
                        ipv4.Add(addr);
                    }
                }

                if (ipv4.Count == 0)
                {
                    return null;
                }

                // 如果远端是 IPv4，优先找与其同网段（简单按前三个字节匹配 /24）
                if (remoteIp != null && remoteIp.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
                {
                    byte[] remoteBytes = remoteIp.GetAddressBytes();
                    foreach (var addr in ipv4)
                    {
                        byte[] localBytes = addr.GetAddressBytes();
                        if (localBytes.Length == 4 &&
                            localBytes[0] == remoteBytes[0] &&
                            localBytes[1] == remoteBytes[1] &&
                            localBytes[2] == remoteBytes[2])
                        {
                            selected = addr;
                            break;
                        }
                    }
                }

                // 其次，选择一个典型内网地址 10.x / 172.16-31.x / 192.168.x.x
                if (selected == null)
                {
                    foreach (var addr in ipv4)
                    {
                        byte[] b = addr.GetAddressBytes();
                        if (b[0] == 10 ||
                            (b[0] == 172 && b[1] >= 16 && b[1] <= 31) ||
                            (b[0] == 192 && b[1] == 168))
                        {
                            selected = addr;
                            break;
                        }
                    }
                }

                // 如果还没选上，就退回第一个 IPv4
                if (selected == null)
                {
                    selected = ipv4[0];
                }

                return selected.ToString();
            }
            catch
            {
            }

            return null;
        }

        private static void TryEnsureFirewallRule()
        {
            try
            {
                string ruleName = "IPP Discovery UDP " + ListenPort;

                // 使用 netsh 添加入站规则，允许 UDP 指定端口
                // 如果规则已存在，netsh 会返回错误，但不影响程序运行，这里忽略返回码
                var psi = new ProcessStartInfo
                {
                    FileName = "netsh",
                    Arguments = "advfirewall firewall add rule name=\"" + ruleName +
                                "\" dir=in action=allow protocol=UDP localport=" + ListenPort,
                    CreateNoWindow = true,
                    UseShellExecute = false,
                    RedirectStandardOutput = true,
                    RedirectStandardError = true
                };

                using (var proc = Process.Start(psi))
                {
                    if (proc == null)
                    {
                        return;
                    }

                    string output = proc.StandardOutput.ReadToEnd();
                    string error = proc.StandardError.ReadToEnd();
                    proc.WaitForExit();

                    Console.WriteLine("[Server] 尝试添加防火墙规则: {0}", ruleName);
                    if (!string.IsNullOrWhiteSpace(output))
                    {
                        Console.WriteLine("[Server][netsh 输出]: {0}", output.Trim());
                    }
                    if (!string.IsNullOrWhiteSpace(error))
                    {
                        Console.WriteLine("[Server][netsh 错误]: {0}", error.Trim());
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("[Server] 添加防火墙规则失败: " + ex.Message);
            }
        }
    }
}


